//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <limits.h>
#include "CActiveTimer.h"

PInterface CActiveTimer::Probe(REIID riid)
{
    if (riid == EIID_IInterface) {
        return (PInterface)this;
    }
    else if (riid == EIID_IActiveTimer) {
        return (IActiveTimer *)this;
    }
    else {
        return NULL;
    }
}

UInt32 CActiveTimer::AddRef()
{
    return m_cRef.Increment();
}

UInt32 CActiveTimer::Release()
{
    Int32 nRef = m_cRef.Decrement();

    if (0 == nRef) {
        delete this;
    }

    return nRef;
}

ECode CActiveTimer::Aggregate(AggregateType type, PInterface pObj)
{
    return E_NOT_IMPLEMENTED;
}

ECode CActiveTimer::GetDomain(PInterface *ppObj)
{
    return E_NOT_IMPLEMENTED;
}

CActiveTimer::CActiveTimer(
    /* [in] */ Millisecond32 interval,
    /* [in] */ PVoid pEntry,
    /* [in] */ PVoid pUserData) :
    m_nInterval(interval),
    m_timerRoutine(EventHandler::Make(pUserData, pEntry)),
    m_pTimerThread(NULL),
    m_pEvent(NULL)
{
}

CActiveTimer::~CActiveTimer()
{
    if (NULL != m_pEvent) {
        m_pEvent->Notify(ActiveTimerEvent_Quit);
    }

    if (NULL != m_pTimerThread) {
        m_pTimerThread->Join(INFINITE, NULL);
        m_pTimerThread->Release();
        m_pTimerThread = NULL;
    }

    if (NULL != m_pEvent) {
        m_pEvent->Release();
        m_pEvent = NULL;
    }
}

// Start the timer
//
ECode CActiveTimer::Start()
{
    assert(NULL != m_pEvent);
    return m_pEvent->Notify(ActiveTimerEvent_Start);
}

// Stop the timer
//
ECode CActiveTimer::Stop()
{
    assert(NULL != m_pEvent);
    return m_pEvent->Notify(ActiveTimerEvent_Stop);
}

// Set the interval, and it will take effect after the timer starts / restarts
//
ECode CActiveTimer::SetInterval(
    /* [in] */ Millisecond32 interval)
{
    if (interval < CTIMER_MINIMUM_PERIOD) {
        return E_INVALID_ARGUMENT;
    }

    m_nInterval = interval;
    return NOERROR;
}

// Get the interval
//
ECode CActiveTimer::GetInterval(
    /* [out] */ Millisecond32 * pInterval)
{
    if (NULL == pInterval) {
        return E_INVALID_ARGUMENT;
    }

    *pInterval = m_nInterval;
    return NOERROR;
}

// Set the priority of the thread on which some routine runs periodically
//
CARAPI CActiveTimer::SetPriority(
    /* [in] */ ThreadPriority priority)
{
    assert(NULL != m_pTimerThread);
    return m_pTimerThread->SetPriority(priority);
}

// Get the priority of the thread on which some routine runs periodically
//
CARAPI CActiveTimer::GetPriority(
    /* [out] */ ThreadPriority * pPriority)
{
    assert(NULL != m_pTimerThread);
    return m_pTimerThread->GetPriority(pPriority);
}

static ECode TimerThread(PVoid arg)
{
    assert(NULL != arg);

    ECode ec;
    WaitResult wr;
    EventState state;
    Millisecond32 startTime, endTime;
    Millisecond32 nWaitingMilliseconds = INT_MAX;

    CActiveTimer * pTimer = (CActiveTimer *)arg;
    PVoid pFuncPtr = pTimer->m_timerRoutine.GetFuncPtr();
    PVoid pThisPtr = pTimer->m_timerRoutine.GetThisPtr();

    while (1) {
        if (INT_MAX == nWaitingMilliseconds) {
            ec = pTimer->m_pEvent->Wait(&wr, &state);
        }
        else {
            ec = pTimer->m_pEvent->TryWait(nWaitingMilliseconds, &wr, &state);
        }

        if (FAILED(ec)) {
            continue;
        }

        if (WaitResult_TimedOut == wr) {
            startTime = _CSystem_GetElapsedTime();
            (*(ECode (ELAPICALLTYPE *)(PVoid, IActiveTimer *))pFuncPtr)(
                pThisPtr, pTimer);
            endTime = _CSystem_GetElapsedTime();

            nWaitingMilliseconds = pTimer->m_nInterval - (endTime - startTime);

            while (nWaitingMilliseconds < 0) {
                nWaitingMilliseconds += pTimer->m_nInterval;
            }
            continue;
        }

        if (ActiveTimerEvent_Start == state) {
            nWaitingMilliseconds = pTimer->m_nInterval;
        }
        else if (ActiveTimerEvent_Stop == state) {
            nWaitingMilliseconds = INT_MAX;
        }
        else if (ActiveTimerEvent_Quit == state) {
            break;
        }
    }
    _CThread_Quit(0);

    assert(0 && "unreachable here");
    return NOERROR;
}

ELAPI _CActiveTimer_New(
    /* [in] */ Millisecond32 interval,
    /* [in] */ ECode (ELAPICALLTYPE *pEntry)(PVoid, IActiveTimer *),
    /* [in] */ PVoid pUserData,
    /* [out] */ IActiveTimer ** ppActiveTimer)
{
    ECode ec;
    CActiveTimer * pActiveTimer;

    if (interval < CTIMER_MINIMUM_PERIOD) {
        return E_INVALID_ARGUMENT;
    }

    if (NULL == ppActiveTimer) {
        return E_INVALID_ARGUMENT;
    }

    pActiveTimer = new CActiveTimer(interval, (PVoid)pEntry, pUserData);
    if (NULL == pActiveTimer) {
        return E_OUT_OF_MEMORY;
    }
    pActiveTimer->AddRef();

    ec = _CEvent_New(FALSE, UNSIGNALED, &pActiveTimer->m_pEvent);
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    ec = _CThread_New(TimerThread, (PVoid)pActiveTimer,
            ThreadCreationFlag_Unstarted, &pActiveTimer->m_pTimerThread);
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    ec = pActiveTimer->m_pTimerThread->Start();
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    *ppActiveTimer = (IActiveTimer *)pActiveTimer;
    return NOERROR;

ErrorExit:
    pActiveTimer->Release();
    return ec;
}
